#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <fcntl.h>
#include <strings.h>
#include <sys/select.h>

int size = sizeof(struct  sockaddr_in);

int main(int argc, char const *argv[])
{
    //买手机（创建网络编口）
    int sockfd = socket(AF_INET , SOCK_STREAM , 0);

    //电话卡（设置IP信息以及端口号）
    // struct sockaddr_in {
    // sa_family_t     sin_family;     /* AF_INET */
    // in_port_t       sin_port;       /* Port number */
    // struct in_addr  sin_addr;       /* IPv4 address */
    // };

    struct  sockaddr_in  my_addr = {0};
    my_addr.sin_family =  AF_INET ;
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    my_addr.sin_port = htons(65003);

    //绑定手机和电话卡
     int ret =  bind(sockfd, (struct sockaddr *)&my_addr,size);
     if(ret)
     {
        perror("bind fail\n");
        return -1;
     }
     else
     {
        printf("bind success\n");
     }


    //设置监听
    if(listen(sockfd, 16)) //实际最大可以接受16+4=20个客户端
    {
        perror("listen fail\n");
        return -1;
    }
    else
    {
        printf("listen success\n");
    }

    //等待连接
    struct sockaddr_in from_addr;
    int conect_sk =  accept(sockfd , (struct sockaddr *)&from_addr,&size);  //如果成功就返回一个新的已连接字,会阻塞等待，如果没有人连接就会一直等待，程序会一直卡在这里
    if(conect_sk == -1)
    {
        perror("conect faile\n");
        return -1;
    }
    else
    {
        printf("connect success\n");
    }

    //采用socket的多路复用IO模型进行信息交流
    
    //1.设置超时时间
    // struct timeval {
    //            time_t      tv_sec;         /* seconds */
    //            suseconds_t tv_usec;        /* microseconds */
    //        };

    struct timeval timeout;
    
    //创建一个集合
    fd_set readfds;     
    FD_ZERO(&readfds);  //清空集合
    char * recvmsg = calloc(1,128);//接受数据
    char * sendmsg = calloc(1,128);//发送数据
    while (1)
    {
        
        timeout.tv_sec = 5; //设置超时时间为5s
        timeout.tv_usec = 0;//设置超时us为0

        FD_SET(conect_sk,&readfds); //将已连接字添加到集合
        FD_SET(STDIN_FILENO,&readfds); //将标准输入添加到集合

        int max_fd = (conect_sk > STDIN_FILENO ? conect_sk : STDIN_FILENO); //找出最大的套接字

        int rect = select(max_fd+1, &readfds, NULL, NULL , &timeout); //等待描述符状态是否变化且是否在规定时间内到达
        if(rect == 0)
        {
            printf("time out\n");  //无数据到达 超时
        }else if(rect == -1)
        {
            printf("error\n");     //发生错误
        }
    
        if(FD_ISSET(conect_sk,&readfds))//检查conect_sk是否还在组内，如果还在组内就意味着状态发生改变，有数据到达
        {
            int ret = recv(conect_sk,recvmsg,128,0);//接收数据
            if(ret > 0 )
            {
                printf("recv_msg from%d:%s\n",conect_sk,recvmsg);
            }
            
        }
        else if(FD_ISSET(STDIN_FILENO,&readfds))//检查STDIN_FILEN是否还在组内，如果还在组内就意味着状态发生改变，有数据输入要发送
        {
            fgets(sendmsg ,128 ,stdin);  //把数据存放到sendmsg
            int ret = send(conect_sk,sendmsg,strlen(sendmsg),0);//发送数据
            if(ret > 0 )
            {
                printf("send_msg :%s\n",recvmsg);
            }

        }


    }
    
    close(sockfd);

    return 0;
   
    

}